class IUP_CONFIG
-- A group of functions to load, store and save application configuration 
-- variables. For example, the list of Recent Files, the last position and size 
-- of a dialog, last used parameters in dialogs, etc.
--
-- Each variable has a key name, a value and a group that it belongs to. The 
-- file is based on a simple configuration file like ".ini" or ".cfg". Each 
-- ground can has more than one key, but all keys in the same group must have 
-- different names. Group and Key names can NOT have a period ".". The file 
-- syntax is such as:
--
-- [Group]
-- Key=Value
-- Key=Value
-- ...
--
-- Guide
--
-- First create a new configuration database using the "config" constructor. To 
-- destroy it use the "destroy" function. Then when the application is started 
-- call "load" and when the application is about to close, call "save".
--
-- To retrieve variables use the corresponding get_+++ functions and after they 
-- where changed store them using the set_+++ functions.
--
-- The following values are used in features to save/retrieve preferences:
--
-- group: group name of the variable
-- key: key name of the variable
-- id: used when the variable has a sequential number
-- value: value of the variable
-- def: default value of the variable
--
-- The features to retrieve variables returns the variable value or empty 
-- string (or 0 for integer and double) if the variable is not set or does not 
-- exist. When the variable may not exist you can use the functions with 
-- "default" to use a default value.
--
-- Internally the variables are stored as attributes using a "<GROUP>.<KEY>" 
-- combination, that's why group and key names can not have periods ".".

inherit
	IUP_WIDGET
		rename
			refresh as config
		redefine
			execute_recent,
			config
		end
	IUP_WIDGET_INTERNALS
		rename
			refresh as config
		redefine
			config
		end

create {ANY}
   config

feature {ANY}

	config
		-- Returns a new database where the variables will be stored.
		local
			a_config: POINTER
		do
			a_config := int_config
			set_widget(a_config)
		end

	-- Operations

	load: INTEGER
		-- Loads the configuration file.
		-- Returns: an error code. 0= no error; -1=error opening the file; 
		-- -2=error accessing the file; -3=error during filename construction
		--
	   -- The filename (with path) can be set using a regular attribute called 
		-- APP_FILENAME.
		--
		-- But the most interesting is to let the filename to be dynamically 
		-- constructed using the APP_NAME attribute. In this case APP_FILENAME 
		-- must not be defined. The file name creation will depend on the system 
		-- and on its usage.
		--
		-- There are two defined usages. First, for a User Configuration File, it 
		-- will be stored on the user Home folder. Second, as an Application 
		-- Configuration File, it will be stored in the same folder of the 
		-- executable.
		--
	   -- The User Configuration File is the most common usage. In UNIX, the 
	   -- filename will be "<HOME>/.<APP_NAME>", where "<HOME>" is replaced by 
		-- the "HOME" environment variable contents, and <APP_NAME> replaced by 
      -- the APP_NAME attribute value. In Windows, the filename will be 
		-- "<HOMEDRIVE><HOMEPATH>\<APP_NAME>.cfg", where HOMEDRIVE and HOMEPATH 
		-- are also obtained from environment variables.
		--
	   -- The Application Configuration File is defined by setting the attribute 
	   -- APP_CONFIG to True (default is False). In this case the attribute 
		-- APP_PATH must also be set. In UNIX, the filename will be 
		-- "<APP_PATH>.<APP_NAME>", and in Windows will be 
		-- "<APP_PATH><APP_NAME>.cfg". Notice that the attribute APP_PATH must 
		-- contains a folder separator "/" at the end.
		--
	   -- After the functions are called the attribute FILENAME is set 
		-- reflecting the constructed filename.
		--
	   -- So usually at start up, an application will do:
		--
   	-- cfg: IUP_CONFIG
		-- create cfg.config
	   -- cfg.set_app_name("MyAppName")
      -- cfg.load
		do
			Result := int_load(widget)
		end

	save: INTEGER
		-- Saves the configuration file.
		-- Returns: an error code. 0= no error; -1=error opening the file; 
		-- -2=error accessing the file; -3=error during filename construction
		--
		-- See the notes at "load" function.
		do
			Result := int_save(widget)
		end

	set_recent_file_menu (menu: IUP_MENU;
                         recent_cb: FUNCTION[TUPLE[IUP_CONFIG], STRING];
                         max_recent: INTEGER)
		-- This function (and the next) store and manage a "Recent Files" menu 
		-- for the application. Call "set_recent_file_menu" once to initialize the
		-- menu. Then every time a file is open or saved call 
		-- "update_recent_file_list" so that the menu list is updated. The last 
		-- file will be always on the top of the list.
		--
		-- Inside the RECENT_CB callback the TITLE attribute contains the 
		-- filename, but the ih object is not the menu, it is the 
		-- IUP_CONFIG object. But also inside the callback the IUP_CONFIG will 
		-- inherit attributes from the menu as if it was its parent.
		--
		-- menu: menu where the recent file items will be listed
		-- recent_cb: agent that will be called when a recent file item is 
		-- selected on the menu
		-- max_recent: the maximum number of recent file items.
		do
			cb_recent := recent_cb
			int_config_recent_init(widget, menu.widget, max_recent)
		end

	set_recent_file_list (list: IUP_LIST;
                         recent_cb: FUNCTION[TUPLE[IUP_CONFIG], STRING];
                         max_recent: INTEGER)
		-- This function store and manage a "Recent Files" list for the 
		-- application. Call "set_recent_file_list" once to initialize the
		-- list. Then every time a file is open or saved call 
		-- "update_recent_file_list" so that the list is updated. The last 
		-- file will be always on the top of the list.
		--
		-- Inside the RECENT_CB callback the TITLE attribute contains the 
		-- filename, but the ih object is not the list, it is the 
		-- IUP_CONFIG object. But also inside the callback the IUP_CONFIG will 
		-- inherit attributes from the list as if it was its parent.
		--
		-- list: list where the recent file items will be listed
		-- recent_cb: agent that will be called when a recent file item is 
		-- selected on the list
		-- max_recent: the maximum number of recent file items.
		do
			cb_recent := recent_cb
			int_config_recent_init(widget, list.widget, max_recent)
		end

	update_recent_file_list (filename: STRING)
		-- Update the recent file menu list.
		--
		-- filename: name of the file that where just saved or open
		do
			int_config_recent_update(widget, filename.to_external)
		end

	show_dialog (dialog: IUP_WIDGET; name: STRING)
		-- This function (and next) store and manage the position and size of a 
		-- dialog. So when the application is run again the dialog can be show at 
		-- its last position and last size. Use this function to show the dialog 
		-- adjusting its size and position. And use the function 
		-- save_dialog_position to save the last dialog position and size when 
		-- the dialog is about to be closed, usually inside the dialog CLOSE_CB 
		-- callback.
		--
		-- load_dialog_position does no adjustments if the dialog is already 
		-- visible, just call "show". If the dialog was closed maximized it will 
		-- be shown maximized. The default size, at the first time ever the 
		-- dialog is shown, is maximized. The dialog size is set only if 
		-- RESIZE=True.
		--
		-- The position is saved in the variables "X" and "Y" of the given group 
		-- name. The size is saved in the variables "Width" and "Height" of the 
		-- given group name.
		--
		-- If your dialog is resizable and you want to avoid the last size usage 
		-- because you changed the dialog layout, then reset the "Width" and 
		-- "Height" variables before calling "load_dialog_position".
		--
		-- To avoid the dialog size to be maximized, set the variable "Maximized" 
		-- to 0 before calling "load_dialog_position".
		--
		-- To use "load_dialog_position" for a modal dialog, call it before 
		-- calling "popup".
		--
		-- dialog: the dialog to manage the size and position
		-- name: a name for this dialog
		do
			int_config_dialog_show (widget, dialog.widget, name.to_external)
		end

	save_dialog (dialog: IUP_WIDGET; name: STRING)
		-- To save the last dialog position and size when the dialog is about to 
		-- be closed, usually inside the dialog CLOSE_CB callback.
		--
		-- dialog: the dialog to manage the size and position
		-- name: a name for this dialog
		do
			int_config_dialog_closed (widget, dialog.widget, name.to_external)
		end

	-- Attributes

	set_app_config (state: BOOLEAN)
		-- Set to True to define an Application Configuration File.
		do
			iup_open.set_attribute(Current, "APP_CONFIG", boolean_to_yesno(state))
		end

	set_app_system_path (state: BOOLEAN)
		-- If set to True, then it will use the system defined folder for 
		-- application files, in Windows will be the same folder given by the 
		-- environment variables with "Application Data\" or "AppData\Roaming\" 
		-- appended.
		do
			iup_open.set_attribute(Current, "APP_SYSTEMPATH", boolean_to_yesno(state))
		end

	set_app_file_name (file_name: STRING)
		-- The name to the configuration file.
		do
			iup_open.set_attribute(Current, "APP_FILENAME", file_name)
		end

	set_app_name (app_name: STRING)
		-- The name of the app, in this case APP_FILE_NAME must not be defined. 
		do
			iup_open.set_attribute(Current, "APP_NAME", app_name)
		end

	set_app_path (app_path: STRING)
		-- For Application Configuration File this attribute must also be set. 
		-- This is the path of the executable folder.
		-- Notice that the path must contains a folder separator "/" at the end.
		do
			iup_open.set_attribute(Current, "APP_PATH", app_path)
		end

	get_file_name: STRING
		-- Path and name of the configuration file.
		do
			Result := iup_open.get_attribute(Current, "FILENAME")
		end

	get_title: STRING
		-- The selected file at recent menu (only inside RECENT_CB callback).
		do
			Result := iup_open.get_attribute(Current, "TITLE")
		end

	-- To store application preferences.

	set_variable_string (group, key, value: STRING)
		do
			int_set_variable_str (widget, group.to_external, key.to_external,
			                      value.to_external)
		end

	set_variable_string_id (group, key: STRING; id: INTEGER; value: STRING)
		do
			int_set_variable_str_id (widget, group.to_external, key.to_external,
			                         id, value.to_external)
		end

	set_variable_integer (group, key: STRING; value: INTEGER)
		do
			int_set_variable_int (widget, group.to_external, key.to_external,
			                      value)
		end

	set_variable_integer_id (group, key: STRING; id, value: INTEGER)
		do
			int_set_variable_int_id (widget, group.to_external, key.to_external,
			                         id, value)
		end

	set_variable_double (group, key: STRING; value: REAL_64)
		do
			int_set_variable_double (widget, group.to_external, key.to_external,
			                         value)
		end

	set_variable_double_id (group, key: STRING; id: INTEGER; value: REAL_64)
		do
			int_set_variable_double_id (widget, group.to_external,
			                            key.to_external, id, value)
		end

	-- To retrieve application preferences

	get_variable_string (group, key: STRING): STRING
		local
			p: POINTER
			str: STRING
		do
			p := int_get_variable_str (widget,
												group.to_external,
												key.to_external)

			if p.is_not_null then
				create str.from_external_copy(p)
				Result := str
			else
				Result := ""
			end
		end

	get_variable_string_id (group, key: STRING; id: INTEGER): STRING
		local
			p: POINTER
			str: STRING
		do
			p := int_get_variable_str_id (widget, group.to_external,
			                              key.to_external, id)

			if p.is_not_null then
				create str.from_external_copy(p)
				Result := str
			else
				Result := ""
			end
		end

	get_variable_integer (group, key: STRING): INTEGER
		do
			Result := int_get_variable_int (widget, group.to_external,
			                                key.to_external)
		end

	get_variable_integer_id (group, key: STRING; id: INTEGER): INTEGER
		do
			Result := int_get_variable_int_id (widget, group.to_external,
			                                   key.to_external, id)
		end

	get_variable_double (group, key: STRING): REAL_64
		do
			Result := int_get_variable_double (widget, group.to_external,
			                                   key.to_external)
		end

	get_variable_double_id (group, key: STRING; id: INTEGER): REAL_64
		do
			Result := int_get_variable_double_id (widget, group.to_external,
			                                      key.to_external, id)
		end

	-- To retrieve application preferences with default values

	get_variable_string_default (group, key, def: STRING): STRING
		local
			p: POINTER
			str: STRING
		do
			p := int_get_variable_str_def (widget, group.to_external,
			                               key.to_external,
			                               def.to_external)

			if p.is_not_null then
				create str.from_external_copy(p)
				Result := str
			else
				Result := ""
			end
		end

	get_variable_string_id_default (group, key: STRING; id: INTEGER;
	                                def: STRING): STRING
		local
			p: POINTER
			str: STRING
		do
			p := int_get_variable_str_id_def (widget, group.to_external,
			                                  key.to_external, id,
			                                  def.to_external)

			if p.is_not_null then
				create str.from_external_copy(p)
				Result := str
			else
				Result := ""
			end
		end

	get_variable_integer_default (group, key: STRING; def: INTEGER): INTEGER
		do
			Result := int_get_variable_int_def (widget, group.to_external,
			                                    key.to_external, def)
		end

	get_variable_integer_id_default (group, key: STRING; id, def: INTEGER): INTEGER
		do
			Result := int_get_variable_int_id_def (widget, group.to_external,
			                                       key.to_external, id, def)
		end

	get_variable_double_default (group, key: STRING; def: REAL_64): REAL_64
		do
			Result := int_get_variable_double_def (widget, group.to_external,
			                                       key.to_external, def)
		end

	get_variable_double_id_default (group, key: STRING; id: INTEGER;
                                   def: REAL_64): REAL_64
		do
			Result := int_get_variable_double_id_def (widget, group.to_external,
			                                          key.to_external, id, def)
		end

feature {IUP}

	execute_recent: STRING
		do
			Result := cb_recent.item([Current])
		end

feature {}

	cb_recent: FUNCTION[TUPLE[IUP_CONFIG], STRING]

	-- Internals

	int_config: POINTER
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfig()"
         }"
      end

	int_load (wgt: POINTER): INTEGER
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigLoad"
         }"
      end

	int_save (wgt: POINTER): INTEGER
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigSave"
         }"
      end

	int_config_recent_init (wgt, menu: POINTER; max_recent: INTEGER)
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "config_recent_init"
         }"
      end

	int_config_recent_update (wgt, filename: POINTER)
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigRecentUpdate"
         }"
      end

	int_config_dialog_show (wgt, dialog, name: POINTER)
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigDialogShow"
         }"
      end

	int_config_dialog_closed (wgt, dialog, name: POINTER)
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigDialogClosed"
         }"
      end

	-- Set variables

	int_set_variable_str (wgt, group, key, value: POINTER)
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigSetVariableStr"
         }"
      end

	int_set_variable_str_id (wgt, group, key: POINTER; id: INTEGER; value: POINTER)
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigSetVariableStrId"
         }"
      end

	int_set_variable_int (wgt, group, key: POINTER; value: INTEGER)
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigSetVariableInt"
         }"
      end

	int_set_variable_int_id (wgt, group, key: POINTER; id, value: INTEGER)
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigSetVariableIntId"
         }"
      end

	int_set_variable_double (wgt, group, key: POINTER; value: REAL_64)
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigSetVariableDouble"
         }"
      end

	int_set_variable_double_id (wgt, group, key: POINTER; id: INTEGER; value: REAL_64)
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigSetVariableDoubleId"
         }"
      end

	-- Get variables

	int_get_variable_str (wgt, group, key: POINTER): POINTER
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigGetVariableStr"
         }"
      end

	int_get_variable_str_id (wgt, group, key: POINTER; id: INTEGER): POINTER
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigGetVariableStrId"
         }"
      end

	int_get_variable_int (wgt, group, key: POINTER): INTEGER
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigGetVariableInt"
         }"
      end

	int_get_variable_int_id (wgt, group, key: POINTER; id: INTEGER): INTEGER
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigGetVariableIntId"
         }"
      end

	int_get_variable_double (wgt, group, key: POINTER): REAL_64
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigGetVariableDouble"
         }"
      end

	int_get_variable_double_id (wgt, group, key: POINTER; id: INTEGER): REAL_64
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigGetVariableDoubleId"
         }"
      end

	-- Get default variables

	int_get_variable_str_def (wgt, group, key, def: POINTER): POINTER
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigGetVariableStrDef"
         }"
      end

	int_get_variable_str_id_def (wgt, group, key: POINTER; id: INTEGER; def: POINTER): POINTER
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigGetVariableStrIdDef"
         }"
      end

	int_get_variable_int_def (wgt, group, key: POINTER; def: INTEGER): INTEGER
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigGetVariableIntDef"
         }"
      end

	int_get_variable_int_id_def (wgt, group, key: POINTER; id, def: INTEGER): INTEGER
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigGetVariableIntIdDef"
         }"
      end

	int_get_variable_double_def (wgt, group, key: POINTER; def: REAL_64): REAL_64
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigGetVariableDoubleDef"
         }"
      end

	int_get_variable_double_id_def (wgt, group, key: POINTER; id: INTEGER; def: REAL_64): REAL_64
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupConfigGetVariableDoubleIdDef"
         }"
      end

end

-- The MIT License (MIT)

-- Copyright (c) 2016, 2018, 2022 by German A. Arias

-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in 
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
